iT邦幫忙

2024 iThome 鐵人賽

DAY 25
1

Day25. Gin的Middleware

今天來看看Gin的Middleware,Middleware是一種中介軟體,可以在處理請求前後做一些事情,例如驗證、日誌、錯誤處理等等。本質上跟你的API handler function 一樣是一個gin.HandlerFunc
你可以設定Global Middleware,或只對特定的路由設定Middleware。

使用Middleware

假設我們現在有一個Middleware叫Logger,用來記錄請求的時間與路徑等request資訊,那麼我們可以透過以下方式設定Middleware

Global

透過func (engine *gin.Engine) Use(middleware ...gin.HandlerFunc) gin.IRoutes進行設置

r.Use(Logger())

Route Specific

如果我們想要針對特定的Route進行Middleware,針對Route或Group進行Middleware的設定方式會有所不同

  • Route
    對於Route, 在你真正處理請求的handler function前加上Middleware Function即可

假設我們要針對/cors設定允許CORS的Middleware, 那麼我們可以這樣設定

func CORSMiddleWare() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
        c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
        c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
        c.Writer.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
        c.Next()
    }
}
  • Group
    r.Group可以用來建立一個Group,它可以把相同prefix的Route放在一起,
    針對這個Group設定Middleware的方式是透過Group.Use進行設定
sequence := r.Group("/sequence")
sequence.Use(Counter())
{
    sequence.GET("/first", func(c *gin.Context) {
        c.String(200, "first")
    })
    sequence.GET("/second", func(c *gin.Context) {
        c.String(200, "second")
    })
}

設定完後sequence這個Group中的所有Route都會套用Counter這個Middleware

Next與Abort

c.Next()

這個函數用來執行下一個Middleware或是handler function,你可以理解成你的Handler function被分成上下兩部分(以c.Next()為分界),上半部分用來初始化,下半部分用來做request後的處理

對於c.Next, 如果有多個Middleware的話c.Next()會依序執行下一個Middleware或是handler function,直到最後一個Middleware或是handler function完成後,再回到上一個Middleware繼續執行。

可以理解成當你呼叫c.Next()時,把目前的handler function放到一個stack中,然後執行下一個Middleware或是handler function,當下一個Middleware或是handler function執行完後,再從stack中取出上一個handler function繼續執行後續部份

func Handler1(c *gin.Context) {
    fmt.Println("Handler1: before next")
    c.Next()
    fmt.Println("Handler1: after next")
}
func Handler2(c *gin.Context) {
    fmt.Println("Handler2: before next")
    c.Next()
    fmt.Println("Handler2: after next")
}
r.GET("/next", Handler1, Handler2, func(c *gin.Context) {
    fmt.Println("Handler3")
    c.JSON(200, gin.H{"message": "next"})
})
// response
/*
Handler1: before next
Handler2: before next
Handler3
Handler2: after next
Handler1: after next
*/

c.Abort()

有時候,處理的結果可能不會是你想要的,又或者某些部份出了問題。這時候你可以透過c.Abort()來停止執行後續的Middleware或是handler function,並且直接回傳結果。然後執行上一個Middleware或是handler function的Next下面的部份。可以理解成透過c.Abort()來將這個handler function作為最後一個handler function執行。

要留意的是,Abort() 要寫在 Next() 之前才有效果,


func cond() bool {
    return true
}

func Handler1(c *gin.Context) {
    fmt.Println("Handler1: before next")
    c.Next()
    fmt.Println("Handler1: after next")
}
func Handler2(c *gin.Context) {
    fmt.Println("Handler2: before next")
    if flag := cond(); flag {
        c.Abort()
    }

    c.Next()
    fmt.Println("Handler2: after next")
}
r.GET("/abort", Handler1, Handler2, func(c *gin.Context) {
    fmt.Println("Handler3")
    c.JSON(200, gin.H{"message": "abort"})
})
// response
/*
Handler1: before next
Handler2: before next
Handler2: after next
Handler1: after next
*/

c.Abort有3個變體,分別是c.Abort(), c.AbortWithStatus(code int), c.AbortWithStatusJSON(code int, jsonObj interface{})

  • c.Abort(): 終止後續的Middleware或是handler function,並且直接回傳結果
  • c.AbortWithStatus(code int): 終止後續的Middleware或是handler function,並且直接回傳指定的HTTP Status Code
  • c.AbortWithStatusJSON(code int, jsonObj interface{}): 終止後續的Middleware或是handler function,並且直接回傳指定的HTTP Status Code與JSON Response
  • c.AbortWithError(code int, err error): 終止後續的Middleware或是handler function,並且直接回傳指定的HTTP Status Code與錯誤訊息

那麼今天的文章就到這告一段落,如果我的文章有任何地方有錯誤請在留言區反應
明天將會講如何在Gin中架設RESTful API
time

REF


上一篇
Day24. Gin上傳檔案
下一篇
Day26. 透過SQL語法與ORM操作資料庫
系列文
可以Go一輩子嗎?31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言